//
//  AssetPlaybackManager.swift
//  MyVideo
//
//  Created by han on 2021/4/22.
//

import UIKit
import AVFoundation

// 播放：主要用来Observer播放情况
class AssetPlaybackManager: NSObject {
    static let shared = AssetPlaybackManager()
    
    weak var delegate: AssetPlaybackDelegate?
    
    private let player = AVPlayer()
    private var readyForPlayback = false
    
    private var playerObserver: NSKeyValueObservation?
    private var playerItemObserver: NSKeyValueObservation?
    private var urlAssetObserver: NSKeyValueObservation?
    
    private var perfMeasurements: PerfMeasurements?
    
    private var playerItem: AVPlayerItem? {
        willSet {
            guard let playerItemObserver = playerItemObserver else { return }
            playerItemObserver.invalidate()
            let notificationCenter = NotificationCenter.default
            notificationCenter.removeObserver(self, name: .TimebaseEffectiveRateChangedNotification, object: playerItem?.timebase)
            notificationCenter.removeObserver(self, name: .AVPlayerItemPlaybackStalled, object: playerItem)
            
            perfMeasurements?.playbackEnded()
        }
        
        didSet {
            playerItemObserver = playerItem?.observe(\AVPlayerItem.status, options: [.new, .initial], changeHandler: { [weak self](playerItem, _) in
                guard let `self` = self else { return }
                if playerItem.status == .readyToPlay {
                    if !self.readyForPlayback {
                        self.readyForPlayback = true
                        self.delegate?.streamPlaybackManager(self, playerReadyToPlay: self.player)
                    }
                } else if playerItem.status == .failed {
                    let error = playerItem.error
                    print("Error: \(String(describing: error?.localizedDescription))")
                }
            })
            
            if let playerItem = playerItem {
                perfMeasurements = PerfMeasurements(playerItem: playerItem)
                
                let notificationCenter = NotificationCenter.default
                notificationCenter.addObserver(self, selector: #selector(handleTimebaseRateChanged(_:)), name: .TimebaseEffectiveRateChangedNotification, object: playerItem.timebase)
                notificationCenter.addObserver(self, selector: #selector(handlePlaybackStalled(_:)), name: .AVPlayerItemPlaybackStalled, object: playerItem)
            }
        }
    }
    
    private var asset: Asset? {
        willSet{
            urlAssetObserver?.invalidate()
        }
        
        didSet {
            if let asset = asset {
                urlAssetObserver = asset.urlAsset.observe(\AVURLAsset.isPlayable, options: [.new, .initial], changeHandler: { [weak self] (urlAsset, _) in
                    guard let `self` = self, urlAsset.isPlayable == true else { return }
                    self.playerItem = AVPlayerItem(asset: urlAsset)
                    self.player.replaceCurrentItem(with: self.playerItem)
                })
            } else {
                playerItem = nil
                player.replaceCurrentItem(with: nil)
                readyForPlayback = false
            }
        }
    }
    
    @objc func handleTimebaseRateChanged(_ notification: Notification) {
        if CMTimebaseGetTypeID() == CFGetTypeID(notification.object as CFTypeRef) {
            let timebase = notification.object as! CMTimebase
            let rate: Double = CMTimebaseGetRate(timebase)
            perfMeasurements?.rateChanged(rate: rate)
        }
    }
    
    @objc func handlePlaybackStalled(_ notification: Notification) {
        perfMeasurements?.playbackStalled()
    }
    
    func setAssetForPlayback(_ asset: Asset?) {
        self.asset = asset
    }
    
    private override init() {
        super.init()
        commonInit()
    }
    
    deinit {
        playerObserver?.invalidate()
    }
    
    
    
}
extension AssetPlaybackManager {
    private func commonInit() {
        playerObserver = player.observe(\AVPlayer.currentItem, options: [.new]) { [weak self] (player, _) in
            guard let `self` = self else { return }
            self.delegate?.streamPlaybackManager(self, playerCurrentItemDidChange: player)
        }
        
        player.usesExternalPlaybackWhileExternalScreenIsActive = true
    }
}

protocol AssetPlaybackDelegate: class {
    func streamPlaybackManager(_ streamPlaybackManager: AssetPlaybackManager, playerReadyToPlay player: AVPlayer)
    func streamPlaybackManager(_ streamPlaybackManager: AssetPlaybackManager, playerCurrentItemDidChange player: AVPlayer)
}

extension Notification.Name {
    static let TimebaseEffectiveRateChangedNotification = Notification.Name(rawValue: kCMTimebaseNotification_EffectiveRateChanged as String)
}
